package route

import (
	myJwt "apiManageGo/src/core/common/jwt"
	"apiManageGo/src/core/common/logs"
	"apiManageGo/src/core/common/ws"
	testController "apiManageGo/src/system/apiManage/controller"
	userController "apiManageGo/src/system/org/user/controller"
	"bytes"
	"encoding/json"
	"github.com/gin-gonic/gin"
	"github.com/satori/go.uuid"
	"go.uber.org/zap"
	"net/http"
	"strings"
)

// 设置跨域中间件
func Options(c *gin.Context) {
	if c.Request.Method != "OPTIONS" {
		c.Next()
	} else {
		c.Header("Access-Control-Allow-Origin", "*")
		c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
		c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
		c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
		c.Header("Content-Type", "application/json")
		c.AbortWithStatus(http.StatusOK)
	}
}

// 设置安全相关的头信息
func Secure(c *gin.Context) {
	c.Header("Access-Control-Allow-Origin", "*")
	//c.Header("X-Frame-Options", "DENY")
	c.Header("X-Content-Type-Options", "nosniff")
	c.Header("X-XSS-Protection", "1; mode=block")
	if c.Request.TLS != nil {
		c.Header("Strict-Transport-Security", "max-age=31536000")
	}
}

type ReturnData struct {
	Code int         `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

// 定义一个responseWriterWrapper类型，用于包裹gin.ResponseWriter，以扩展其功能
type responseWriterWrapper struct {
	gin.ResponseWriter               // 继承gin.ResponseWriter，保留原有功能
	body               *bytes.Buffer // 新增一个缓冲区，用于存储响应体的内容
	statusCode         int           // 用于记录响应的状态码
}

// 重写WriteHeader方法，用于在响应头被写入之前记录状态码
func (w *responseWriterWrapper) WriteHeader(statusCode int) {
	w.statusCode = statusCode                // 记录状态码
	w.ResponseWriter.WriteHeader(statusCode) // 调用原始的WriteHeader方法发送状态码
}

// 重写Write方法，实现在响应体内容被写入时同时缓存这些内容
func (w *responseWriterWrapper) Write(b []byte) (int, error) {
	return w.body.Write(b) // 将响应体内容写入缓冲区进行缓存
	//return w.ResponseWriter.Write(b) // 调用原始的Write方法将内容写入实际的响应体
}

type CustomReadCloser struct {
	*bytes.Reader
}

func (c CustomReadCloser) Close() error {
	return nil
}

// 定义loggingMiddleware中间件，用于在每个请求结束时打印响应的状态码和内容
// 校验 token
// 自动识别响应内容是对象还是数组，并包装成统一的返回格式
func loggingMiddlewareAndRewrite(c *gin.Context) {
	uuid_v4 := uuid.NewV4()
	log_uuid := zap.String("uuid", uuid_v4.String())

	// 创建一个responseWriterWrapper实例，用于替换当前的ResponseWriter
	writer := &responseWriterWrapper{
		ResponseWriter: c.Writer,        // 使用原ResponseWriter初始化
		body:           &bytes.Buffer{}, // 初始化一个空的缓冲区
	}
	c.Writer = writer // 将上下文中的Writer替换为我们自定义的writer

	// 排除的路由直接放行 支持正则匹配

	isUrlExclude := false
	for _, v := range excludeRoute {
		if strings.HasPrefix(c.Request.URL.Path, v) {
			isUrlExclude = true
			break
		}
	}

	//如果是排除的路由，直接放行
	//其他的需要校验token
	if !isUrlExclude {
		// 请求头中获取token
		token := c.GetHeader("Authorization")
		if token == "" {
			Error_401(writer)
			c.Abort()
			return
		}

		_, token_err := myJwt.CheckToken(token)
		if token_err != nil {
			Error_401(writer)
			c.Abort()
			return
		}
	}

	defer func() {
		writer.body.Reset() // 重置缓冲区以备后续请求使用
	}()

	contentType := c.Request.Header.Get("Content-Type")
	//如果 application/json 请求
	if strings.HasPrefix(contentType, "application/json") {
		//获取Body字符串
		bodyBytes, err := c.GetRawData()
		if err != nil {
			logs.Error("获取请求Body失败:", zap.Error(err))
		}
		//放回body
		c.Request.Body = CustomReadCloser{bytes.NewReader(bodyBytes)}
		logs.Debug("Request Body:", zap.Any("body", string(bodyBytes)), log_uuid)
	}

	// 继续执行后续的请求处理链
	c.Next()

	// 在所有的处理完成后，可以从writer中获取并打印响应的状态码和内容
	status := writer.statusCode
	if status == 404 {
		Error_404(writer)
		return
	}
	body := writer.body
	//打印url
	logs.Debug("Request URL:", zap.String("url", c.Request.URL.String()), log_uuid)
	logs.Debug("Request Method:", zap.String("method", c.Request.Method), log_uuid)

	//打印请求头
	headers := map[string]string{}
	for k, v := range c.Request.Header {
		headers[k] = v[0]
	}
	if err := c.Request.ParseForm(); err != nil {
		logs.Error("解析表单参数失败:", zap.Error(err))
	}
	logs.Debug("Request Header:", zap.Any("headers", headers), log_uuid)
	//获取所有的表单参数
	form := map[string]string{}
	for k, v := range c.Request.Form {
		form[k] = v[0]
	}
	logs.Debug("Request Form:", zap.Any("form", form), log_uuid)
	logs.Debug("Response Status:", zap.Int("status", status), log_uuid)       // 打印状态码
	logs.Debug("Response Body:", zap.String("body", body.String()), log_uuid) // 将缓冲区内容转换为字符串并打印

	if len(c.Errors) > 0 {
		//获取最后一个error 返回
		err := c.Errors.Last()
		Error(err.Error(), writer)
		return
	}

	//判断响应内容是对象还是数组，并包装成统一的返回格式
	var dataMap map[string]interface{}
	err := json.Unmarshal(body.Bytes(), &dataMap)
	if err == nil {
		//对象
		Success(dataMap, writer)
		return
	} else {
		//数组
		var dataArray []interface{}
		err = json.Unmarshal(body.Bytes(), &dataArray)
		if err == nil {
			Success(dataArray, writer)
			return
		}
	}

	//未知类型，直接返回请求原来的内容
	writer.ResponseWriter.Write(body.Bytes())
}

// 成功返回
func Success(data interface{}, writer *responseWriterWrapper) {
	returnData := ReturnData{
		Code: 0,
		Msg:  "success",
		Data: data,
	}
	newbody, e := json.Marshal(returnData)
	if e != nil {
		logs.Error("json序列化失败:", zap.Error(e))
	} else {
		//写入新内容
		writer.ResponseWriter.Write(newbody)
	}
}

// 失败返回
func Error(msg string, writer *responseWriterWrapper) {
	returnData := ReturnData{
		Code: -1,
		Msg:  msg,
		Data: nil,
	}
	newbody, e := json.Marshal(returnData)
	if e != nil {
		logs.Error("json序列化失败:", zap.Error(e))
	} else {
		//写入新内容
		writer.ResponseWriter.Write(newbody)
	}
}
func Error_404(writer *responseWriterWrapper) {
	returnData := ReturnData{
		Code: 404,
		Msg:  "没有找到该资源",
		Data: nil,
	}
	newbody, e := json.Marshal(returnData)
	if e != nil {
		logs.Error("json序列化失败:", zap.Error(e))
	} else {
		//写入新内容
		writer.ResponseWriter.Write(newbody)
	}
}
func Error_403(writer *responseWriterWrapper) {
	returnData := ReturnData{
		Code: 403,
		Msg:  "没有权限访问",
		Data: nil,
	}
	newbody, e := json.Marshal(returnData)
	if e != nil {
		logs.Error("json序列化失败:", zap.Error(e))
	} else {
		//写入新内容
		writer.ResponseWriter.Write(newbody)
	}
}
func Error_401(writer *responseWriterWrapper) {
	returnData := ReturnData{
		Code: 401,
		Msg:  "凭证失效，请重新登录",
		Data: nil,
	}
	newbody, e := json.Marshal(returnData)
	if e != nil {
		logs.Error("json序列化失败:", zap.Error(e))
	} else {
		//写入新内容
		writer.ResponseWriter.Write(newbody)
	}
}

// 排除的路由
var excludeRoute = []string{
	"/api/user/login",
	"/api/user/register",
	"/api/test/*",
}

func Route(engine *gin.Engine) {
	engine.Use(Options)
	engine.Use(Secure)
	engine.Use(loggingMiddlewareAndRewrite)
	//原生路由注册方式
	//engine.GET("/hello", controller.GetTest)
	engine.GET("/ws", ws.Ws.OpenWebSocket)
	//自动注册方式
	AutoRoute(engine, "/api/user", userController.NewUserController())
	AutoRoute(engine, "/api/test", testController.NewHelloController())
	//兼容原先gin拦截方式，自己写好LoginRequired，在LoginRequired之后的AutoRoute都会被拦截
	//engine.Use(LoginRequired)
	//AutoRoute(engine, "/filter", controller.NewHelloController())
}
